package com.xishu.controller;

import com.xishu.annotation.valid.MethodValidator;
import com.xishu.bo.LockOrderCashOrOrderItemBo;
import com.xishu.config.Constant;
import com.xishu.entity.User;
import com.xishu.entity.oplog.OrderOpLog;
import com.xishu.entity.order.Order;
import com.xishu.entity.order.OrderItem;
import com.xishu.entity.order.PartCash;
import com.xishu.entity.order.PayTypeLock;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseData;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.CommonService;
import com.xishu.service.LockService;
import com.xishu.service.OrderOpLogService;
import com.xishu.service.order.base.OrderService;
import com.xishu.service.PartPayService;
import com.xishu.util.ClassUtil;
import com.xishu.util.ServletUtil;
import com.xishu.util.Tools;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.*;

/**
 * 订单状态变化
 */
@RestController
public class OrderStatusController implements Tools {
    private static Logger logger = LoggerFactory.getLogger(OrderStatusController.class);

    private CommonService commonService = CommonService.getInstance();
    private LockService localLockService = LockService.getInstance();
    private OrderService orderService = OrderService.getInstance();

    @ApiOperation(value = "进入分钱结账")
    @PutMapping(value = ("/user/order/enter/part/cash"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData enterPartCash(@RequestBody Order order) throws Exception {
        return setStatus(order,
                new Integer[]{Constant.ORDER_STATUS_STARTING, Constant.ORDER_STATUS_SETTING_PART_CASH, Constant.ORDER_STATUS_PART_CASH_CUSTOMIZED, ORDER_STATUS_PART_CASH, ORDER_STATUS_PART_CASH_PAYED},
                Constant.ORDER_STATUS_SETTING_PART_CASH, (orderInDb) -> {
                    //进入分钱结账日志
                    OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                    OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, TO_PART_CASH);
                    orderOpLogService.toPartCash(orderInDb, orderOpLog);


                    //如果是B端用
                });
    }

    @ApiOperation(value = "进入分菜结账")
    @PutMapping(value = ("/user/order/enter/part/food"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData enterFoodCash(@RequestBody Order order) throws Exception {
        return setStatus(order,
                new Integer[]{ORDER_STATUS_STARTING, ORDER_STATUS_SETTING_PART_FOOD, ORDER_STATUS_PART_FOOD_PAYED},
                ORDER_STATUS_SETTING_PART_FOOD, (orderInDb) -> {
                    //如果是服务员端在操作，创建一个子订单，并将里面的菜全部展开
                    if (ServletUtil.getUserId() != null) {
                        try {
                            //只添加已经确认好的菜
                            orderInDb = (Order) ClassUtil.clone(orderInDb);
                            List<OrderItem> orderItemList = orderInDb.getOrderItemDetailList().stream().filter(p -> getBoolean(p.getCheckout()) && getBoolean(p.getConfirmItem())).collect(Collectors.toList());
                            orderInDb.setOrderItemDetailList(orderItemList);

                            Order subOrder = orderService.createSubOrder(orderInDb);
                            subOrder.setOrderItemDetailList(new ArrayList<>());
                            //依次添加菜
                            for (OrderItem orderItem : orderInDb.getOrderItemDetailList()) {
                                for (Integer i = 0; i < orderItem.getCount(); i++) {
                                    OrderItem cloneOrderItem = (OrderItem) ClassUtil.clone(orderItem);
                                    cloneOrderItem.setCount(1);
                                    subOrder.getOrderItemDetailList().add(cloneOrderItem);
                                }
                            }

                            orderService.calPrice(subOrder);

                            commonService.directSave(subOrder);

                        } catch (Exception e) {
                            logger.error("e", e);
                        }
                    }
                });
    }

    @ApiOperation(value = "设置成为一个人付钱")
    @PutMapping(value = ("/user/order/enter/one/user/pay"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData enterOneUserPay(@RequestBody Order order) throws Exception {
        User user = ServletUtil.getUser();

        if (user != null) {
            return enterOneUserPayInB(order);
        } else {
            return setStatus(order,
                    new Integer[]{Constant.ORDER_STATUS_STARTING, ORDER_STATUS_ONE_PERSON_PAYING},
                    ORDER_STATUS_ONE_PERSON_PAYING, null);
        }
    }

    @ApiOperation(value = "客人修改按人数分钱结账的配置")
    @PutMapping(value = ("/user/order/part/cash"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData partCash(@RequestBody Order reqOrder) throws Exception {

        VerifyUtil.verify(() -> reqOrder.getId() != null && reqOrder.getShopId() != null);
        Long orderId = reqOrder.getId();
        Long shopId = reqOrder.getShopId();
        VerifyUtil.verify(() -> shopId != null && orderId != null);
        String lockKey = orderService.getLockKey(shopId, orderId);
        //只传part cash就可以了
        PartCash partCash = reqOrder.getPartCash();

        VerifyUtil.verify(() -> getInt(partCash.getPersons()) <= 20,PART_CASH_PERSON_NUMBER);
        VerifyUtil.verify(() -> getInt(partCash.getPersons()) >= 1);

        int status = ORDER_STATUS_PART_CASH;

        Object lock = localLockService.getLock(lockKey);
        synchronized (lock) {
            Order orderInDb = orderService.findOrder(shopId, orderId, false);
            //只有正在结账的时候，可以来设置
            //从进入分钱结账到设置分钱结账
            User user = ServletUtil.getUser();
            logger.info("order is {}", orderInDb);

            if (user == null) {
                validStatus(orderInDb, Arrays.asList(ORDER_STATUS_SETTING_PART_CASH));
            }

            logger.info("part cash is {}", reqOrder.getPartCash());
            changeOrderStatus(status, orderInDb);

            orderService.showPrice(orderInDb, null);
            orderService.setPartCash(orderInDb, reqOrder.getPartCash());
            logger.info("part cash is {}", reqOrder.getPartCash());

            //如果是按人数分钱，那么创建对应的子订单
            if (ServletUtil.getUserId() != null && getInt(reqOrder.getPartCash().getPersons()) > 0) {
                orderService.addSubOrders(orderInDb);
            }

            return commonService.createResponse(orderInDb);
        }
    }

    private ResponseData enterOneUserPayInB(Order order) throws Exception {
        Long shopId = order.getShopId();
        Long orderId = order.getId();
        String lockKey = orderService.getLockKey(shopId, orderId);
        Object lock = localLockService.getLock(lockKey);
        synchronized (lock) {
            logger.info("order lock by waiter");
            Order orderInDb = orderService.findOrder(shopId, orderId, false);
            VerifyUtil.verify(() -> !orderService.isOrderAtLeastPayed(orderInDb), ORDER_PAYED);
            orderInDb.setStatus(ORDER_STATUS_ONE_PERSON_PAYING);
            orderInDb.setLastOperateSession(ServletUtil.getSessionId());
            orderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_ONE_USER);
            orderInDb.setLockByWaiter(true);
            commonService.save(orderInDb);
            return commonService.createResponse(orderInDb);
        }
    }


    /**
     * 设置一个状态
     *
     * @param order
     * @param status
     * @return
     * @throws Exception
     */
    public ResponseData setStatus(Order order, Integer[] validStatusArray, Integer status, Consumer<Order> callback) throws Exception {
        VerifyUtil.verify(() -> order.getShopId() != null && order.getId() != null);
        String lockKey = orderService.getLockKey(order);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            Order orderInDb = orderService.findOrder(order);
            User user = ServletUtil.getUser();
            if (user != null) {
                logger.info("will lock by waiter");
                orderInDb.setLockByWaiter(true);
                ResponseData responseData = changeOrderStatus(status, orderInDb);
                if (callback != null) callback.accept(orderInDb);
                return responseData;
            } else {
                setStatusInC(validStatusArray, status, orderInDb);
                changeOrderStatus(status, orderInDb);
            }

            return commonService.createResponse(orderInDb);
        }
    }

    public ResponseData changeOrderStatus(int status, Order orderInDb) throws Exception {
        VerifyUtil.verify(() -> orderInDb.getStatus() != ORDER_STATUS_END, ORDER_PAYED);

        //没有设置过，则设置一下
        if (getInt(orderInDb.getStatus()) <= status) {
            orderInDb.setStatus(status);
            logger.info("will set order status to {}", status);

            //设置用户选择付款方式
            if (status == ORDER_STATUS_ONE_PERSON_PAYING) {
                logger.info("set order status to ORDER_STATUS_ONE_PERSON_PAYING");
                orderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_ONE_USER);
                //进入全部结账
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, USER_ALL_PAY);
                orderOpLogService.userAllPay(orderInDb,orderOpLog);
            } else if (status == ORDER_STATUS_PART_CASH) {
                logger.info("set order status to ORDER_STATUS_PART_CASH");
                orderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON);
                //按照人数分钱结账
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, PART_CASH_PERSON);
                orderOpLogService.PartCashPersonLog(orderInDb,orderOpLog);
            } else if (status == ORDER_STATUS_SETTING_PART_CASH) {
                //如果是分钱结账，默认是自定义分钱结账
                logger.info("set order status to ORDER_STATUS_PART_CASH_CUSTOMIZED");
                orderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED);
                //进入分钱结账
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, TO_PART_CASH);
                orderOpLogService.toPartCash(orderInDb,orderOpLog);

            } else if (status == ORDER_STATUS_SETTING_PART_FOOD) {
                logger.info("set order status to ORDER_STATUS_SETTING_PART_FOOD");
                orderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_PART_FOOD);
                //进入分菜结账
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, PART_FOOD);
                orderOpLogService.toPartItem(orderInDb,orderOpLog);

            }
        }

        commonService.save(orderInDb);
        return commonService.createResponse(orderInDb);
    }

    /**
     * C端修改订单状态
     *
     * @param validStatusArray
     * @param status
     * @param orderInDb
     * @return
     * @throws Exception
     */
    private void setStatusInC(Integer[] validStatusArray, Integer status, Order orderInDb) throws Exception {
        //如果是C端进来，B端已经锁定了，那么给提示
        if (getBoolean(orderInDb.getLockByWaiter())) {
            VerifyUtil.throwError(WAITER_OPERATE_THE_ORDER);
        }

        //只有正在结账的时候，可以来设置
        validStatus(orderInDb, Arrays.asList(validStatusArray));

        //如果是C端的一个人结账，那么需要判断是否是这个人拿到了锁，拿到了延长时间，没有拿到锁，说明有其它用户进来了，那么退出
        if (status == ORDER_STATUS_ONE_PERSON_PAYING) {
            logger.info("deal one user pay, add lock time");
            String sessionId = ServletUtil.getSessionId();
            PayTypeLock payTypeLock = orderInDb.getPayTypeLock();
            //撤消了订单，并且还没有人选择支付方式
            VerifyUtil.verify(() -> payTypeLock != null, ResponseStatus.ORDER_TIME_OUT);
            //VerifyUtil.throwError(ResponseStatus.ORDER_TIME_OUT);
            //其它用户拿到了支付方式的锁
            VerifyUtil.verify(() -> equals(payTypeLock.getLockSessionId(), sessionId), ResponseStatus.ORDER_TIME_OUT);
            //正常拿到锁的用户，延长锁定的时间
            payTypeLock.setLockTime(System.currentTimeMillis());

            //成功锁定了，那么添加一个人锁定所有金额
            LockOrderCashOrOrderItemBo lockOrderCashOrOrderItemBo = new LockOrderCashOrOrderItemBo();
            lockOrderCashOrOrderItemBo.setShopId(orderInDb.getShopId());
            lockOrderCashOrOrderItemBo.setOrderId(orderInDb.getId());
            lockOrderCashOrOrderItemBo.setLockCash(true);
            lockOrderCashOrOrderItemBo.setCash(formatMoneyDouble(orderInDb.getUserCost()));
            PartPayService.getInstance().oneUserPayLock(lockOrderCashOrOrderItemBo, orderInDb);
        }

        logger.info("order in db status is {}, request status is {}", orderInDb.getStatus(), status);
        if (getInt(orderInDb.getStatus()) <= status) {
            //保存状态
            orderInDb.setStatus(status);
            commonService.save(orderInDb);
        }
    }

    /**
     * 判断订单是否在合法的状态范围内
     *
     * @param orderInDb
     * @param statusList
     */
    public void validStatus(Order orderInDb, List<Integer> statusList) throws ResponseStatusException {
        VerifyUtil.verify(() -> orderInDb != null);
        boolean present = statusList.stream().filter(p -> p.intValue() == orderInDb.getStatus().intValue()).findAny().isPresent();
        if (!present) {
            //根据订单的状态来显示状态
            if (getInt(orderInDb.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_ONE_USER) {
                VerifyUtil.throwError(LOCK_SELECT_PAY_ALL);
            } else if (getInt(orderInDb.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON) {
                VerifyUtil.throwError(LOCK_SELECT_PART_CASH_BY_PERSON);
            } else if (getInt(orderInDb.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED) {
                VerifyUtil.throwError(LOCK_SELECT_PART_CASH_CUSTOMIZED);
            } else if (getInt(orderInDb.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_FOOD) {
                VerifyUtil.throwError(LOCK_SELECT_PART_FOOD);
            }
        }

        VerifyUtil.verify(() -> present, ResponseStatus.OTHER_USER_OPERATE_THE_ORDER);
    }


}
